package com.rc.saas.tenant.controller.transfer;

import com.alibaba.fastjson.JSON;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.github.dozermapper.core.Mapper;
import com.rc.saas.tenant.base.TenantBaseController;
import com.rc.saas.tenant.model.acct.AcctWithdrawOrder;
import com.rc.saas.tenant.model.transfer.MemberRefundApply;
import com.rc.saas.tenant.service.acct.AcctWithdrawOrderService;
import com.rc.saas.tenant.service.transfer.MemberRefundApplyService;
import com.rc.saas.tenant.transfers.alipay.config.AliPayConfig;
import com.rc.saas.tenant.transfers.alipay.vo.AlipayTransNotifyVo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * 支付宝转账到支付宝账户
 * https://opensupport.alipay.com/support/helpcenter/113/201602481109?ant_source=zsearch#
 * Created by Sven on 2021/02/02
 */
@Controller
@Scope("prototype")
@RequestMapping(value = "/alipay/transfer")
public class AlipayTransferController extends TenantBaseController {
    private static Logger logger = LogManager.getLogger(AlipayTransferController.class);

    //https://opendocs.alipay.com/open/common/fr9vsk
    //商户接收异步通知成功，并返回 success 数据给支付宝。
    //http状态码：200，表示异步发送成功，商户端接收到异步成功。
    //返回：success，表示商户接收到异步返回的信息为 success。如果是返回信息为其他的，返回后显示商户返回的数据。
    //说明：返回信息不是 success，支付宝会重复发送异步通知（25 小时内发送 8 次），商户可以重新接收异步通知，返回数据。

    //响应值
    //响应值	描述	是否重试
    //fail	消息获取失败	重试
    //success	消息获取成功	不重试
    final String FAIL = "fail";
    final String SUCCESS = "success";

    final String TRADE_SUCCESS = "SUCCESS";

    @Autowired
    private Mapper dozerMapper;

    @Autowired
    private AliPayConfig aliPayProperties;

    @Autowired
    private AliPayConfig aliPayConfig;

    @Autowired
    private MemberRefundApplyService memberRefundApplyService;

    @Autowired
    private AcctWithdrawOrderService acctWithdrawOrderService;

    /**
     * http://16c11903n8.iask.in:26955/alipay/transfer/transPayWalletNotify
     * 转账到支付宝账户中单据状态变更后触发的 alipay.fund.trans.order.changed （转账单据状态变更通知 ）,需要在支付宝开放平台设置“应用网关”
     * shiro不能拦截,shiro.ini文件增加:/alipay/transfer/transPayWalletNotify = anon
     * 转账单据状态变更通知 alipay.fund.trans.order.changed
     * https://opendocs.alipay.com/open/309/106236
     */
    @ResponseBody
    @RequestMapping(value = "/transPayWalletNotify", method = RequestMethod.POST)
    public synchronized String alipayNotify(HttpServletRequest request, HttpServletResponse response) {
        String url = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
                + request.getServletPath();

        logger.info("支付宝 transPayWalletNotify 入帐通知:{}", url);

        //获取支付宝POST过来反馈信息
        Map<String, String> parameters = new HashMap<String, String>();
        Map requestParams = request.getParameterMap();
        logger.info("支付宝 transPayWalletNotify 异步通知入参1:{}", requestParams);
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";

            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }

            //乱码解决，这段代码在出现乱码时使用。
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            parameters.put(name, valueStr);
        }


        logger.info("支付宝 transPayWalletNotify 异步通知入参2:{}", parameters);

        //rsaCheckV1会删除这两个key，先保存，需手工补上
        String sign_type = parameters.get("sign_type");
        String sign = parameters.get("sign");

        //消息验签
        //在公钥证书模式下，支付宝开放平台 SDK 提供了 AlipaySignature.rsaCertCheckV1 方法，可以使用该方法对通知报文验签。
        //AlipaySignature.rsaCheckV1是适用于老版本的RSA加密
        //AlipaySignature.rsaCheckV2才是适用于新版本的RSA2
        //现在我们生产秘钥时一般都是生产新的加密方式RSA2

        //配置是支付宝公钥?
        //boolean result = AlipaySignature.rsaCertCheckV2(parameters, aliPayProperties.getCertPath(), "utf-8", "RSA2");

        try {
            //开放平台SDK提供了AlipaySignature.rsaCheckV1()与AlipaySignature.rsaCheckV2()两个版本。
            // V1验签时会剔除sign_type参数，V2会保留sign_type参数。V2仅适用于生活号接口
            boolean flag = AlipaySignature.rsaCertCheckV1(parameters, aliPayConfig.getPublicCertPath(), aliPayProperties.getCharset(), aliPayProperties.getSignType());
            if (!flag) {
                logger.error("支付宝 transPayWalletNotify 支付异步通知验签失败");
                return FAIL;
            }
        } catch (Exception e) {
            logger.error("支付宝 transPayWalletNotify 支付异步通知验签失败", e);
            return FAIL;
        }

        //返回客户端支付信息
        String bizContent = parameters.get("biz_content");
        AlipayTransNotifyVo alipayNotifyVo = JSON.parseObject(bizContent, AlipayTransNotifyVo.class);
        logger.info("支付宝 transPayWalletNotify接口JSON信息:{}", bizContent);

        if (TRADE_SUCCESS.equals(alipayNotifyVo.getStatus())) {

            //再赋值
            parameters.put("sign_type", sign_type);
            parameters.put("sign", sign);

            try {

                //查转帐单
                AcctWithdrawOrder acctWithdrawOrder = acctWithdrawOrderService.selectAcctWithdrawOrderByOutTradeNo(alipayNotifyVo.getOut_biz_no());
                if (acctWithdrawOrder == null) {
                    logger.error("商户端订单号out_biz_no不存在:{}", alipayNotifyVo.getOut_biz_no());
                    return FAIL;
                }
                //到帐状态 0未到帐  1已到帐 2转账失败 默认0
                if (!"1".equals(acctWithdrawOrder.getStatus())) {
                    //金额验证1:buyer_pay_amount 用户在交易中支付的金额，如果支付通道做补贴活动，客户实际支付的金额会少于商品金额
                    if (!String.valueOf(acctWithdrawOrder.getAmount()).equals(String.valueOf(alipayNotifyVo.getTrans_amount()))) {
                        logger.error("商家在交易中实际收到的款项跟转帐金额不一致，:{},:{}", alipayNotifyVo.getOut_biz_no(), acctWithdrawOrder.getAmount() + ":" + alipayNotifyVo.getTrans_amount());
                        return FAIL;
                    }

                    MemberRefundApply info = memberRefundApplyService.selectByOutOrderNo(acctWithdrawOrder.getOutTradeNo());

                    //AlipayTransNotifyVo 转换为 AlipayFundTransUniTransferResponse
                    AlipayFundTransUniTransferResponse transferResponse = dozerMapper.map(alipayNotifyVo, AlipayFundTransUniTransferResponse.class);

                    //入账处理
                    acctWithdrawOrderService.updateSuccessByTransfer(transferResponse, acctWithdrawOrder.getWithdrawOrderId(),info.getRefundApplyId());
//                    acctWithdrawOrderService.updateSuccessAcctWithdrawOrder(transferResponse, acctWithdrawOrder.getWithdrawOrderId());

                    logger.info("****支付宝转帐异步通知处理成功****:{},:{}", alipayNotifyVo.getOut_biz_no(), acctWithdrawOrder.getAmount());
                    return SUCCESS;
                } else {
                    //如果已入帐
                    logger.info("支付宝转帐异步通知已入帐,:{},:{}", alipayNotifyVo.getOut_biz_no(), acctWithdrawOrder.getAmount());
                    return SUCCESS;
                }

            } catch (Exception e) {
                logger.error("支付宝转帐异步通知处理错误", e);
                return FAIL;
            }

        } else {
            logger.error("transPayWalletNotify:status标记不为 SUCCESS:{},:{}", parameters.get("out_biz_no"), parameters.get("status"));
            return FAIL;
        }
    }
}
